// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/transform/exprs/constant_expr.h>
#include <cantor/transform/utility.h>

namespace cantor::transform {
    void TransConstantExpr::resolve_ast(const cantor::parser::ConstantExpr *node) {
        if (!node) {
            status = turbo::data_loss_error("no expr");
            return;
        }
        switch (node->literal_type) {
            case pb::ast::CET_INT:
                const_expr.set_int_value(node->_u.int64_val);
                const_expr.set_type(pb::ast::CET_INT);
                break;
            case pb::ast::CET_DOUBLE:
                const_expr.set_double_value(node->_u.double_val);
                const_expr.set_type(pb::ast::CET_DOUBLE);
                break;
            case pb::ast::CET_STRING:
                const_expr.set_str_value(std::move(node->_u.str_val.to_string()));
                const_expr.set_type(pb::ast::CET_STRING);
                break;
            case pb::ast::CET_BOOL:
                const_expr.set_bool_value(node->_u.bool_val);
                const_expr.set_type(pb::ast::CET_BOOL);
                break;
            case pb::ast::CET_NULL:
                const_expr.mutable_null_value()->Clear();
                const_expr.set_type(pb::ast::CET_NULL);
                break;
            case pb::ast::CET_PLACE_HOLDER:
                const_expr.mutable_holder()->Clear();
                const_expr.set_type(pb::ast::CET_PLACE_HOLDER);
                break;
            case pb::ast::CET_HEX:
                const_expr.mutable_hex_value()->set_value(node->_u.str_val.to_string());
                const_expr.set_type(pb::ast::CET_HEX);
                break;
            case pb::ast::CET_BIT:
                const_expr.mutable_bit()->set_value(node->_u.str_val.to_string());
                const_expr.set_type(pb::ast::CET_BIT);
                break;
            case pb::ast::CET_MAXVALUE:
                const_expr.mutable_max_value()->Clear();
                const_expr.set_type(pb::ast::CET_MAXVALUE);
                break;
            case pb::ast::CET_MINVALUE:
                const_expr.mutable_min_value()->Clear();
                const_expr.set_type(pb::ast::CET_MINVALUE);
                break;
            case pb::ast::CET_NONE:
                status = turbo::data_loss_error("unset expr with NONE flag");
                const_expr.set_type(pb::ast::CET_NONE);
            default:
                status = turbo::data_loss_error("unset expr with unknown flag");
                const_expr.set_type(pb::ast::CET_UNKNOWN);
                break;
        }
        set_proto_location(node, const_expr.mutable_location());
        set_proto_status(const_expr.mutable_status());
    }

    pb::ast::ConstantExprType TransConstantExpr::get_type() const {
        if (const_expr.has_type()) {
            return const_expr.type();
        }
        return pb::ast::CET_UNKNOWN;
    }

    const turbo::Status &TransConstantExpr::check() {
        if (!status.ok()) {
            return status;
        }
        if (!const_expr.has_location()) {
            make_missing_required_field("location");
            return status;
        }
        if (!const_expr.has_type()) {
            make_missing_required_field("type");
            return status;
        }
        switch (const_expr.type()) {
            case pb::ast::CET_INT:
                if (!const_expr.has_int_value()) {
                    make_missing_required_field("int_value");
                }
                return status;
            case pb::ast::CET_DOUBLE:
                if (!const_expr.has_double_value()) {
                    make_missing_required_field("double_value");
                }
                return status;
            case pb::ast::CET_STRING:
                if (!const_expr.has_str_value()) {
                    make_missing_required_field("str_value");
                }
                return status;
            case pb::ast::CET_BOOL:
                if (!const_expr.has_bool_value()) {
                    make_missing_required_field("bool_value");
                }
                return status;
            case pb::ast::CET_NULL:
                if (!const_expr.has_null_value()) {
                    make_missing_required_field("null_value");
                }
                return status;
            case pb::ast::CET_PLACE_HOLDER:
                if (!const_expr.has_str_value()) {
                    make_missing_required_field("str_value");
                }
                return status;
            case pb::ast::CET_HEX:
                if (!const_expr.has_hex_value()) {
                    make_missing_required_field("hex_value");
                }
                return status;
            case pb::ast::CET_BIT:
                if (!const_expr.has_bit()) {
                    make_missing_required_field("bit_value");
                }
                return status;
            case pb::ast::CET_MAXVALUE:
                if (!const_expr.has_max_value()) {
                    make_missing_required_field("max_value");
                }
                return status;
            case pb::ast::CET_MINVALUE:
                if (!const_expr.has_min_value()) {
                    make_missing_required_field("min_value");
                }
                return status;
            case pb::ast::CET_NONE:
                status = turbo::data_loss_error("unset expr with NONE flag");
                return status;
            default:
                status = turbo::data_loss_error("unset expr with unknown flag");
                return status;
        }
        TURBO_UNREACHABLE();
    }

    turbo::Status TransConstantExpr::resolve_to(turbo::Nonnull<const cantor::parser::ConstantExpr*> node, turbo::Nonnull<pb::ast::ConstantExpr*>out) {
        TransConstantExpr translator;
        translator.resolve_ast(node);
        auto status = translator.check();
        if (!status.ok()) {
            return status;
        }
        *out = std::move(translator.const_expr);
        return status;
    }
} // namespace cantor
